//
//  XLogWrapper.swift
//  Runner
//
//  Created by zzzzzzz on 2020/10/28.
//

import Foundation
import Dispatch
import Flutter

public class XLogWrapper: NSObject, FlutterPlugin{
  

    public static var channel:FlutterMethodChannel!;
   
   public static func register(with registrar: FlutterPluginRegistrar) {
       print("this is register")
       channel = FlutterMethodChannel(name: "com.qw.flutter.xlog.plugins/xlog", binaryMessenger: registrar.messenger())
       let instance = XLogWrapper()
       registrar.addMethodCallDelegate(instance, channel: channel)
   }
    
//    private func invokeMethod( method:String, arguments:Any) {
//      if XLogWrapper.channel != nil {
//          XLogWrapper.channel.invokeMethod(method, arguments: arguments)
//          }
//      }
      
    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        
        let methodName = call.method;
        let arguments = call.arguments as? Dictionary<String,AnyObject>

       
    
        switch methodName {
            
        case "init":
            let isConsoleLogOpen = toBoolValue( number: (arguments?["isConsoleLogOpen"] != nil));
            
//                let level = arguments?["logTag"]
            XLogWrapper.pub_path = arguments?["logPath"] as! String;
            let encryptPubKey = arguments?["encryptPubKey"];
            XLogWrapper.pub_key = encryptPubKey as! String;
            XLogWrapper.getInstance().initXlogWrapper(isConsoleLogOpen: isConsoleLogOpen)
            result(true);
            break;
            
        case "log":
            
                let level = arguments?["level"]
                  let tag = arguments?["tag"]
                  let msg = arguments?["msg"]
                  let isIMLog = toBoolValue(number: (arguments?["isIMLog"] != nil));
                  XLogWrapper.getInstance().printLog(isIMLog: isIMLog, level: level as! String, tag: tag as! String, log: msg as! String)
            result(true);
            break;
            
        case "appenderFlush":
            
            appenderFlush();
            
            result(true);
            break;
            
        default:
            break;
        }
        
        
        
    }
    
    /*
     * 日志文件前缀
     */
    private let LOG_NAME_PREFIX = "kanjian";

    /**
     * IM相关日志TAG
     */
    public let IM_LOG_TAG = "xlog_mars::app_im: ";

    /**
     * IM以外的其它业务日志TAG
     */
    public let BIZ_LOG_TAG = "xlog_mars_biz: ";
    
    public static var pub_key =   "";
    public static var pub_path =   "";
    
    /**
     * 单条日志输出最大长度
     * xlog单条日志控制台最大输出2kb
     * 700*3(汉字最大3字节)=2100字节,保证每条日志都能完整输出在控制台
     * <p>
     * xlog保存日志到文件,单条日志最大限制是16kb,能满足大多数日志的输出
     * 所以仅在debug时才分割日志,显示在控制台
     */
    private let MAX_LENGTH = 700;
    /**
     * 日志加密公钥
     * 公钥要与so编译时使用的一致,否则log不能加密
     */
    private let LOG_PUB_KEY = "0f8f3869f18feb304a98b5048273f2d20e707ca85438ea4311a8aa6f5e3117154fb692eaf3703" +
            "82ed1e04aab3735a244f7405ab5659b3155d95402c345520451";
    
    
    
    private static var ourInstance  =  XLogWrapper();

    public static func getInstance() -> XLogWrapper{
        return ourInstance;
    }
    


    
    /**
       * 是否已经初始化
       */
      private var isInit = false;
      /**
       * 控制台是否打印日志
       */
      private var isConsoleLogOpen = false;

//      private XLogWrapper() {
//      }

      /**
       * 初始化
       *
       * @param context
       * @param isConsoleLogOpen 控制台是否显示日志(正式发包设置为false)
       */
    
        //context:CGContext,
    public  func initXlogWrapper(isConsoleLogOpen:Bool) {
//            com.qw.flutter.xlog.plugins/xlog
            
            let lockQueue = DispatchQueue(label: "im.XlogWrapper.init", attributes: .concurrent)
            
            lockQueue.sync {
                if (!isInit) {
                    self.isConsoleLogOpen = isConsoleLogOpen;
                    do {
                        //初始化日志组件
                        
                        //                                System.loadLibrary("c++_shared");
                        //                                System.loadLibrary("marsxlog");
                        
                        var logPath = XLogWrapper.getLogPath(isConsoleLogOpen: isConsoleLogOpen);
                        var cachePath = XLogWrapper.getCachePath(isConsoleLogOpen: isConsoleLogOpen);
                        
                        
                        let jmb = JinkeyMarsBridge()
                      
                        jmb.initXlogger(.debug, releaseLevel: .info, path: XLogWrapper.pub_path, prefix: "xlog",key: XLogWrapper.pub_key)
                       
//                              jmb.log(.debug, tag: "JinkeyXlogger", content: "github Jinkeycode")
                          
                        JinkeyMarsBridge.log(.debug, tag: "JinkeyXlogger", message: "github Jinkeycode")
                        //正式模式下:只保存info以上级别的日志
//                        Xlog.appenderOpen(isConsoleLogOpen ? Xlog.LEVEL_ALL : Xlog.LEVEL_ALL, Xlog.AppednerModeAsync, cachePath, logPath,
//                                          LOG_NAME_PREFIX, 0, LOG_PUB_KEY);
//                      
//                     
//
//
//                        Xlog.setConsoleLogOpen(isConsoleLogOpen);
//                        Log.setLogImp(new Xlog());
//                        appender_set_console_log(true);
//                        
//                        appender_open(kAppednerAsync, [logPath UTF8String], logConfig.nameprefix.UTF8String, pubKey);
                        
//                        appender_open
                        
                        isInit = true;
//                        XXLog.i(tag: "XLogWrapper", msg: "xlog init finish");
                        
                        XLogWrapper.getInstance().printLog(isIMLog: false, level: "i", tag: "XLogWrapper", log: "xlog init finish")
                    } catch (let error){
                        
                       print(error.localizedDescription)
                    }
                }
            }
            
      
      }


      /**
       * 对长日志进行分割打印
       *
       * @param log
       */
    public func printLog(isIMLog:Bool, level:String,tag:String,log:String) {
        
        
        let newTag = BIZ_LOG_TAG+tag;
        
          if (isInit && log != "") {
            
//           let newTag = (isIMLog ? IM_LOG_TAG : BIZ_LOG_TAG) + tag;

              if (!isConsoleLogOpen) {
                  //线上环境不需要输出到控制台:不用分割日志直接保存(单条日志最大16kb)
                self.log(level: level, tag: newTag, msg: log);
                  return;
              }

            let length = log.count;
            if (length <= MAX_LENGTH) {
                self.log(level: level, tag: tag, msg: log);
              } else {
                do {
                      var start = 0;
                      var end = MAX_LENGTH;

                      while (true) {
                          //剩下的文本还是大于规定长度则继续重复截取并输出
                          if (length > end) {
                            
                            let starIndex = log.index(log.startIndex, offsetBy: start)
                            let endIndex = log.index(log.startIndex, offsetBy: end)
                             self.log(level: level, tag: tag, msg: String(log[starIndex..<endIndex]));
                              
                            
                            start = end;
                            end = end + MAX_LENGTH;
                          } else {
                            let starIndex = log.index(log.startIndex, offsetBy: start)
                            let endIndex = log.index(log.startIndex, offsetBy: length)
                            self.log(level: level, tag: tag, msg: String(log[starIndex..<endIndex]));
                              break;
                          }
                      }
                } catch let error {
//                    XXLog.e(tag: "XXXXLog", msg: error.localizedDescription);
                  }
              }
          }
      }

    private func log( level:String,  tag:String, msg:String) {
        if ("e" == level) {
            XXLog.e(tag:tag,msg:msg)
   
          } else if ("w"==level) {
            XXLog.w(tag: tag, msg: msg);
             
          } else if ("i"==level) {
            XXLog.i(tag: tag, msg: msg);
            
          } else if ("v"==level) {
            XXLog.v(tag: tag, msg: msg);
             
          } else {
            XXLog.d(tag: tag, msg: msg);
              
          }
      }

      /**
       * 当日志写入模式为异步时，调用该接口会把内存中的日志写入到文件
       * isSync : true 为同步 flush，flush 结束后才会返回。 false 为异步 flush，不等待 flush 结束就返回
       * 每次登录房间时触发
       */
      public func  appenderFlush() {
        
        //synchronized
        
        
          if (isInit) {
//              JinkeyMarsBridge().appender_flush();
              JinkeyMarsBridge.appenderFlush(false);
          }
      }

      public func onDestroy() {
        
        //synchronized
        
          if (isInit) {
//              Log.appenderFlush(false);
//              deinitXlogger();
              JinkeyMarsBridge.appenderFlush(false);
              isInit = false;
          }
      }


      /**
       * 日志保存的目录
       * 如果没有获取存储权限,mars会自动存到getLogCachePath()目录
       *
       * @param context
       * @param isConsoleLogOpen
       * @return
       */
    private static func getLogPath(isConsoleLogOpen:Bool)->String {
        var path:String;
        
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        
        let documentDirectory = paths[0]
        let documentDirectoryUrl = URL.init(string: documentDirectory)
        path = documentDirectoryUrl!.absoluteString;
        
        
        if (isConsoleLogOpen) {
            return "\(path)/TT_app/xlog/debug";
        } else {
            return "\(path)/TT_app/xlog/release";
        }
      }

    private static func getCachePath(isConsoleLogOpen:Bool)->String {
        
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let documentDirectory = paths[0]
        let documentDirectoryUrl = URL.init(string: documentDirectory)
        
        if (isConsoleLogOpen) {
            return "\(documentDirectoryUrl!.absoluteString)/xlog/debug";
          } else {
            return "\(documentDirectoryUrl!.absoluteString)/xlog/release";
          }
      }
    
    
    public  func toBoolValue(number:Bool?)->Bool {
        return number ?? false;
      }

    
}
